github.com/aarzilli/tools@v0.0.0-20151123112009-0d27094f75e0/dsu/persistent_cursor/query with cursor.go (about)

     1  // Package persistent_cursor shows
     2  // how a query iterator has a cursor object;
     3  // that can be serialized into memcache
     4  // and reused to continue the query in
     5  // a different request
     6  package persistent_cursor
     7  
     8  import (
     9  	"bytes"
    10  	"fmt"
    11  	"net/http"
    12  	"time"
    13  
    14  	"appengine"
    15  	ds "appengine/datastore"
    16  	"appengine/memcache"
    17  
    18  	"github.com/pbberlin/tools/net/http/loghttp"
    19  	"github.com/pbberlin/tools/stringspb"
    20  
    21  	gbp "github.com/pbberlin/tools/dsu/ancestored_gb_entries" // guest book persistence
    22  )
    23  
    24  func guestViewCursor(w http.ResponseWriter, r *http.Request, m map[string]interface{}) {
    25  
    26  	c := appengine.NewContext(r)
    27  
    28  	q := ds.NewQuery(gbp.GbEntryKind)
    29  	q.Order("-Date")
    30  
    31  	b1 := new(bytes.Buffer)
    32  
    33  	cur_start, err := memcache.Get(c, "greeting_cursor")
    34  	if err == nil {
    35  		str_curs := string(cur_start.Value)
    36  		if len(cur_start.Value) > 0 {
    37  			cursor, err := ds.DecodeCursor(str_curs) //  inverse is string()
    38  			loghttp.E(w, r, err, false)
    39  			if err == nil {
    40  				b1.WriteString("found cursor from memcache -" + stringspb.Ellipsoider(str_curs, 10) + "-<br>\n")
    41  				q = q.Start(cursor)
    42  			}
    43  		}
    44  	}
    45  
    46  	iter := q.Run(c)
    47  	var cntr int = 0
    48  	for {
    49  		var g gbp.GbEntryRetr
    50  		cntr++
    51  		if cntr > 2 {
    52  			b1.WriteString("  batch complete -" + string(cntr) + "-<br>\n")
    53  			break
    54  
    55  		}
    56  
    57  		_, err := iter.Next(&g)
    58  		if err == ds.Done {
    59  			b1.WriteString("scan complete -" + string(cntr) + "-<br>\n")
    60  			break
    61  		}
    62  
    63  		if fmt.Sprintf("%T", err) == fmt.Sprintf("%T", new(ds.ErrFieldMismatch)) {
    64  			err = nil // ignore this one - it's caused by our deliberate differences between gbsaveEntry and gbEntrieRetr
    65  		}
    66  
    67  		if err != nil {
    68  			b1.WriteString("error fetching next: " + err.Error() + "<br>\n")
    69  			break
    70  		}
    71  
    72  		b1.WriteString("  - " + g.String())
    73  	}
    74  
    75  	// Get updated cursor and store it for next time.
    76  	if cur_end, err := iter.Cursor(); err == nil {
    77  
    78  		str_c_end := cur_end.String() //  inverse is decode()
    79  		val := []byte(str_c_end)
    80  
    81  		mi_save := &memcache.Item{
    82  			Key:        "greeting_cursor",
    83  			Value:      val,
    84  			Expiration: 60 * time.Second,
    85  		}
    86  
    87  		if err := memcache.Set(c, mi_save); err != nil {
    88  			b1.WriteString("error adding memcache item " + err.Error() + "<br>\n")
    89  		} else {
    90  			b1.WriteString("wrote cursor to memcache -" + stringspb.Ellipsoider(str_c_end, 10) + "-<br>\n")
    91  		}
    92  
    93  	} else {
    94  		b1.WriteString("could not retrieve cursor_end " + err.Error() + "<br>\n")
    95  	}
    96  
    97  	w.Header().Set("Content-Type", "text/html")
    98  	w.Write(b1.Bytes())
    99  
   100  	w.Write([]byte("<br>----<br>"))
   101  
   102  }
   103  
   104  func init() {
   105  	http.HandleFunc("/guest-view-cursor", loghttp.Adapter(guestViewCursor))
   106  }